#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
VirtualFileSystem - VFSClient的具体实现
"""

import hashlib
import io
import os
import sys
import logging
from typing import List, Optional, Union, BinaryIO, Dict, Any
from datetime import datetime, timedelta

# 添加项目根目录到路径
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if project_root not in sys.path:
    sys.path.insert(0, project_root)

from src.common.database import init_global_session
from ..models.file import VirtualFile, VirtualFileRepository
from ..models.storage import VirtualStorage, VirtualStorageRepository
from .node import VFSNode


class VirtualFileSystem:
    """虚拟文件系统实现"""
    
    def __init__(self, clouds_client=None):
        """初始化虚拟文件系统
        
        Args:
            clouds_client: 云存储客户端实例，用于天翼网盘操作
        """
        # 初始化日志
        self.logger = logging.getLogger(__name__)
        
        # 确保全局session已初始化
        init_global_session()
        
        # 创建Repository实例（不再需要传入session）
        self.file_repo = VirtualFileRepository()
        self.storage_repo = VirtualStorageRepository()
        
        # 获取全局session的引用
        from src.common.database import get_session
        self.session = get_session()
        
        # 集成clouds_client
        self.clouds_client = clouds_client
        
        # 天翼网盘配置（TODO: 从配置文件或环境变量读取）
        self.tianyi_provider = 'tianyi_pc'
        self.tianyi_username = None  # TODO: 配置默认账号
        
    def set_tianyi_account(self, username: str):
        """设置天翼网盘账号"""
        self.tianyi_username = username
        self.logger.info(f"VFS设置天翼账号: {username}, CloudsClient: {self.clouds_client is not None}")
        
        # 尝试从数据库加载token
        self._load_token_from_database(username)

    def _load_token_from_database(self, username: str) -> bool:
        """从数据库加载token信息"""
        try:
            from src.models.cloud import CloudAccount
            from datetime import datetime
            
            # 查找云盘账号
            cloud_account = self.session.query(CloudAccount).filter_by(
                cloud_provider='tianyi',
                cloud_account=username,
                is_active=True
            ).first()
            
            if not cloud_account:
                self.logger.warning(f"未找到云盘账号: {username}")
                return False
            
            # 检查是否有有效的token
            has_valid_token = False
            if (cloud_account.access_token and cloud_account.session_key and 
                cloud_account.token_expires_at and 
                datetime.utcnow() < cloud_account.token_expires_at):
                has_valid_token = True
                self.logger.info(f"发现有效的缓存token: {username}")
            else:
                self.logger.info(f"没有有效的缓存token: {username}")
                return False
            
            if has_valid_token and self.clouds_client:
                try:
                    # 使用缓存的token创建持久化信息
                    from src.clouds_sdk.base_client import PersistentLoginInfo
                    
                    # 构造完整的token信息（extra_data包含所有token信息）
                    token_extra_data = {
                        'accessToken': cloud_account.access_token,
                        'sessionKey': cloud_account.session_key,
                        'sessionSecret': cloud_account.session_secret,
                        'refreshToken': cloud_account.refresh_token,
                        'loginName': f"{username}@189.cn",
                        'expireTime': int(cloud_account.token_expires_at.timestamp() * 1000) if cloud_account.token_expires_at else None
                    }
                    
                    persistent_info = PersistentLoginInfo(
                        provider='tianyi_pc',
                        token=cloud_account.access_token,
                        expires_at=cloud_account.token_expires_at,
                        extra_data=token_extra_data
                    )
                    
                    # 尝试使用缓存的token登录
                    success = self.clouds_client.file_system.load_account(
                        'tianyi_pc', username, None, persistent_info, 
                        auto_load_persistent=False, save_persistent=False
                    )
                    
                    if success:
                        self.logger.info(f"使用缓存token登录成功: {username}")
                        return True
                    else:
                        self.logger.warning(f"缓存token登录失败: {username}")
                        return False
                except Exception as token_error:
                    self.logger.warning(f"使用缓存token登录时出错: {token_error}")
                    return False
            else:
                self.logger.info(f"CloudsClient未初始化或没有有效token: {username}")
                return False
                
        except Exception as e:
            self.logger.error(f"从数据库加载token失败: {e}")
            return False

    def _save_token_to_database(self, username: str) -> bool:
        """保存token信息到数据库"""
        try:
            from src.models.cloud import CloudAccount
            from datetime import datetime, timedelta
            
            if not self.clouds_client:
                self.logger.warning("CloudsClient未初始化，无法保存token")
                return False
            
            # 查找云盘账号
            cloud_account = self.session.query(CloudAccount).filter_by(
                cloud_provider='tianyi',
                cloud_account=username,
                is_active=True
            ).first()
            
            if not cloud_account:
                self.logger.warning(f"未找到云盘账号: {username}")
                return False
            
            # 尝试从clouds_client获取token信息
            try:
                # 获取当前活跃的客户端
                account_key = f"tianyi_pc_{username}"
                if hasattr(self.clouds_client, 'file_system') and self.clouds_client.file_system:
                    client = self.clouds_client.file_system.clients.get(account_key)
                    if client and hasattr(client, 'token_info') and client.token_info:
                        token_info = client.token_info
                        
                        # 保存token信息
                        cloud_account.access_token = token_info.get('accessToken')
                        cloud_account.session_key = token_info.get('sessionKey')
                        cloud_account.session_secret = token_info.get('sessionSecret')
                        cloud_account.refresh_token = token_info.get('refreshToken')
                        cloud_account.last_login_at = datetime.utcnow()
                        
                        # 设置token过期时间
                        if token_info.get('expireTime'):
                            cloud_account.token_expires_at = datetime.fromtimestamp(token_info.get('expireTime') / 1000)
                        else:
                            # 默认24小时过期
                            cloud_account.token_expires_at = datetime.utcnow() + timedelta(hours=24)
                        
                        self.session.commit()
                        self.logger.info(f"Token信息已保存到数据库: {username}")
                        return True
                    else:
                        self.logger.warning(f"无法获取客户端token信息: {username}")
                        return False
                else:
                    self.logger.warning("CloudsClient file_system未初始化")
                    return False
                    
            except Exception as token_e:
                self.logger.error(f"获取token信息失败: {token_e}")
                return False
                
        except Exception as e:
            self.logger.error(f"保存token到数据库失败: {e}")
            self.session.rollback()
            return False

    def _on_login_success(self, username: str):
        """登录成功后的回调，保存token到数据库"""
        self.logger.info(f"登录成功，保存token到数据库: {username}")
        self._save_token_to_database(username)
    
    def _ensure_valid_token(self, username: str) -> bool:
        """确保token有效，如果无效则尝试重新登录"""
        try:
            if not self.clouds_client:
                self.logger.warning("CloudsClient未初始化，无法验证token")
                return False
            
            # 检查当前token是否有效
            try:
                account_info = self.clouds_client.file_system.get_account_info("tianyi_pc", username)
                if account_info:
                    self.logger.debug(f"Token仍然有效: {username}")
                    return True
            except Exception as e:
                self.logger.warning(f"Token验证失败: {username} - {e}")
            
            # Token无效，尝试重新登录
            self.logger.info(f"Token无效，尝试重新登录: {username}")
            return self._re_login_with_password(username)
            
        except Exception as e:
            self.logger.error(f"确保token有效时出错: {e}")
            return False
    
    def _re_login_with_password(self, username: str) -> bool:
        """使用密码重新登录"""
        try:
            from src.models.cloud import CloudAccount
            
            # 从数据库获取账号密码
            cloud_account = self.session.query(CloudAccount).filter_by(
                cloud_provider='tianyi',
                cloud_account=username,
                is_active=True
            ).first()
            
            if not cloud_account:
                self.logger.error(f"未找到云盘账号: {username}")
                return False
            
            if not cloud_account.cloud_password:
                self.logger.error(f"账号密码为空: {username}")
                return False
            
            # 使用密码重新登录
            login_success = self.clouds_client.load_account(
                provider="tianyi_pc",
                username=username,
                password=cloud_account.cloud_password,
                auto_load_persistent=False,
                save_persistent=False
            )
            
            if login_success:
                self.clouds_client.set_account("tianyi_pc", username)
                # 登录成功后保存token到数据库
                self._on_login_success(username)
                self.logger.info(f"密码重新登录成功: {username}")
                return True
            else:
                self.logger.error(f"密码重新登录失败: {username}")
                return False
                
        except Exception as e:
            self.logger.error(f"重新登录时出错: {e}")
            return False
    
    # ==============================
    # 文件上传：秒传检测和普通上传
    # ==============================
    
    def check_quick_upload(self, file_md5: str) -> bool:
        """
        检测是否支持秒传（根据文件 MD5 判断系统中是否存在相同内容）
        """
        storage = self.storage_repo.find_by_file_id(file_md5)
        return storage is not None
    
    def quick_upload(self, file_md5: str, vfs_path: str, overwrite: bool = True, **meta) -> int:
        """
        秒传：不上传文件内容，仅创建虚拟文件引用
        
        Args:
            file_md5: 文件内容的MD5
            vfs_path: 虚拟目标路径
            overwrite: 是否覆盖现有文件，默认为True（WebDAV PUT语义）
            **meta: 附加元数据
        """
        # 检查存储是否存在
        storage = self.storage_repo.find_by_file_id(file_md5)
        if not storage:
            raise FileNotFoundError(f"Storage with MD5 {file_md5} not found")
        
        # 解析路径
        parent_path, filename = self._split_path(vfs_path)
        
        # 处理根目录的情况
        if parent_path == "/":
            parent_id = 1  # 根目录的ID是1
        else:
            parent_file = self._resolve_path(parent_path)
            if not parent_file:
                raise FileNotFoundError(f"Parent directory not found: {parent_path}")
            parent_id = parent_file.id
        
        # 检查文件是否已存在
        existing_file = self.file_repo.find_by_name_and_parent(filename, parent_id)
        if existing_file and not existing_file.deleted:
            if not overwrite:
                raise FileExistsError(f"File {filename} already exists")
            else:
                # 覆盖模式：删除现有文件
                self.delete(vfs_path, force=True)
        
        # 创建虚拟文件
        virtual_file = self.file_repo.create_file(
            name=filename,
            parent_id=parent_id,
            file_size_bytes=storage.file_size_bytes,
            media_type=storage.media_type,
            file_extension=storage.file_extension,
            storage_file_id=storage.file_id
        )
        
        # 增加引用计数
        self.storage_repo.increment_reference_count(storage.id)
        
        self.session.commit()
        return 0
    
    def upload_file_stream(self, file_stream: BinaryIO, vfs_path: str, overwrite: bool = True, **meta) -> int:
        """
        流式上传：支持流式文件上传，适用于大文件和WebDAV协议
        
        Args:
            file_stream: 可读的文件流对象
            vfs_path: 虚拟目标路径
            overwrite: 是否覆盖现有文件，默认为True（WebDAV PUT语义）
            **meta: 附加元数据
        """
        import tempfile
        import os
        
        # 使用临时文件来处理流式上传
        temp_file = None
        try:
            # 创建临时文件
            temp_file = tempfile.NamedTemporaryFile(delete=False)
            
            # 分块读取流并写入临时文件，同时计算MD5
            md5_hash = hashlib.md5()
            chunk_size = 8192  # 8KB chunks
            total_size = 0
            
            while True:
                chunk = file_stream.read(chunk_size)
                if not chunk:
                    break
                temp_file.write(chunk)
                md5_hash.update(chunk)
                total_size += len(chunk)
            
            temp_file.close()
            file_md5 = md5_hash.hexdigest()
            
            # 解析路径
            parent_path, filename = self._split_path(vfs_path)
            
            # 确保父目录存在
            if parent_path != "/":
                try:
                    self.mkdir(parent_path)
                except:
                    pass  # 目录可能已存在，忽略错误
            
            # 处理根目录的情况
            if parent_path == "/":
                parent_id = 1  # 根目录的ID是1
            else:
                parent_file = self._resolve_path(parent_path)
                if not parent_file:
                    raise FileNotFoundError(f"Parent directory not found: {parent_path}")
                parent_id = parent_file.id
            
            # 简化文件覆盖逻辑：直接删除现有文件
            existing_file = self.file_repo.find_by_name_and_parent(filename, parent_id)
            if existing_file and not existing_file.deleted and overwrite:
                try:
                    self.delete(vfs_path, force=True)
                except:
                    pass  # 忽略删除错误
            
            # 获取文件扩展名
            file_extension = None
            if '.' in filename:
                file_extension = filename.split('.')[-1].lower()
            
            # 检查是否已存在相同MD5的存储（秒传检查）
            existing_storage = self.storage_repo.find_by_file_id(file_md5)
            if existing_storage:
                self.logger.info(f"发现相同MD5的存储，直接使用秒传: {file_md5}")
                # 秒传：直接创建虚拟文件引用，不检查云存储（避免慢查询）
                virtual_file = self.file_repo.create_file(
                    name=filename,
                    parent_id=parent_id,
                    file_size_bytes=total_size,
                    media_type=meta.get('media_type', 0),
                    file_extension=file_extension,
                    storage_file_id=file_md5
                )
                # 增加引用计数
                self.storage_repo.increment_reference_count(existing_storage.id)
                self.session.commit()
                return 0
            
            # 集成clouds_client上传到天翼网盘
            if self.clouds_client and self.tianyi_username:
                self.logger.info(f"开始上传文件到云存储: {filename}, MD5: {file_md5}")
                try:
                    # 读取临时文件内容
                    with open(temp_file.name, 'rb') as f:
                        file_data = f.read()
                    
                    # 确保/nas_cache目录存在
                    nas_cache_dir = "/nas_cache"
                    if not self.clouds_client.exists(nas_cache_dir):
                        self.logger.info(f"创建nas_cache目录: {nas_cache_dir}")
                        self.clouds_client.mkdir(nas_cache_dir)
                    
                    # 使用时间目录管理器确保时间目录存在
                    from src.clouds_sdk.cloud_storage_time_manager import CloudStorageCleaner
                    cleaner = CloudStorageCleaner(self.clouds_client)
                    time_dir_path = cleaner.ensure_time_dir_exists(nas_cache_dir)
                    
                    if not time_dir_path:
                        self.logger.error("创建时间目录失败")
                        cloud_file_id = None
                    else:
                        # 构造云存储路径（使用时间目录）
                        cloud_filename = f"{file_md5.upper()}_{filename}"
                        cloud_path = f"{time_dir_path}/{cloud_filename}"
                        
                        self.logger.info(f"上传文件到云存储路径: {cloud_path}")
                        
                        # 上传文件到云存储
                        upload_success = self.clouds_client.upload_file(file_data, cloud_path)
                        if upload_success:
                            self.logger.info(f"文件上传到云存储成功: {cloud_path}")
                            cloud_file_id = cloud_filename  # 使用云存储文件名作为文件ID
                        else:
                            self.logger.error(f"文件上传到云存储失败: {cloud_path}")
                            cloud_file_id = None
                except Exception as e:
                    self.logger.error(f"云存储上传异常: {e}")
                    cloud_file_id = None
            else:
                self.logger.warning(f"CloudsClient或天翼账号未初始化: clouds_client={self.clouds_client is not None}, tianyi_username={self.tianyi_username}")
                cloud_file_id = None
            
            # 创建存储记录
            storage = self.storage_repo.create_storage(
                file_id=file_md5,
                original_filename=filename,
                file_size_bytes=total_size,
                media_type=meta.get('media_type', 0),
                cloud_provider='tianyi',
                file_extension=file_extension,
                cloud_file_id=cloud_file_id,
                local_data=None
            )
            
            # 创建虚拟文件
            virtual_file = self.file_repo.create_file(
                name=filename,
                parent_id=parent_id,
                file_size_bytes=total_size,
                media_type=meta.get('media_type', 0),
                file_extension=file_extension,
                storage_file_id=file_md5
            )
            
            self.session.commit()
            return 0
            
        except Exception as e:
            self.session.rollback()
            raise e
        finally:
            # 清理临时文件
            if temp_file and os.path.exists(temp_file.name):
                try:
                    os.unlink(temp_file.name)
                except:
                    pass
    
    def upload_file(self, file_stream: Union[bytes, BinaryIO], vfs_path: str, overwrite: bool = True, **meta) -> int:
        """
        普通上传：传入完整文件字节流，上传到天翼网盘并创建虚拟文件
        
        Args:
            file_stream: 文件内容（bytes 或可读 BinaryIO）
            vfs_path: 虚拟目标路径
            overwrite: 是否覆盖现有文件，默认为True（WebDAV PUT语义）
            **meta: 附加元数据
        """
        # 读取文件内容
        if isinstance(file_stream, bytes):
            file_data = file_stream
        else:
            file_data = file_stream.read()
        
        # 计算MD5
        file_md5 = hashlib.md5(file_data).hexdigest()
        
        # 解析路径
        parent_path, filename = self._split_path(vfs_path)
        
        # 确保父目录存在
        if parent_path != "/":
            try:
                self.mkdir(parent_path)
            except:
                pass  # 目录可能已存在，忽略错误
        
        # 处理根目录的情况
        if parent_path == "/":
            parent_id = 1  # 根目录的ID是1
        else:
            parent_file = self._resolve_path(parent_path)
            if not parent_file:
                raise FileNotFoundError(f"Parent directory not found: {parent_path}")
            parent_id = parent_file.id
        
        # 简化文件覆盖逻辑：直接删除现有文件
        existing_file = self.file_repo.find_by_name_and_parent(filename, parent_id)
        if existing_file and not existing_file.deleted and overwrite:
            try:
                self.delete(vfs_path, force=True)
            except:
                pass  # 忽略删除错误
        
        # 获取文件扩展名
        file_extension = None
        if '.' in filename:
            file_extension = filename.split('.')[-1].lower()
        
        # 检查是否已存在相同MD5的存储（秒传检查）
        existing_storage = self.storage_repo.find_by_file_id(file_md5)
        if existing_storage:
            # 秒传：直接创建虚拟文件引用
            virtual_file = self.file_repo.create_file(
                name=filename,
                parent_id=parent_id,
                file_size_bytes=len(file_data),
                media_type=meta.get('media_type', 0),
                file_extension=file_extension,
                storage_file_id=file_md5
            )
            # 增加引用计数
            self.storage_repo.increment_reference_count(existing_storage.id)
            self.session.commit()
            return 0
        
        # 集成clouds_client上传到天翼网盘
        if self.clouds_client and self.tianyi_username:
            self.logger.info(f"开始上传文件到云存储: {filename}, MD5: {file_md5}")
            try:
                nas_cache_dir = "/nas_cache"
                if not self.clouds_client.exists(nas_cache_dir):
                    self.logger.info(f"创建nas_cache目录: {nas_cache_dir}")
                    self.clouds_client.mkdir(nas_cache_dir)
                
                # 使用时间目录管理器确保时间目录存在
                from src.clouds_sdk.cloud_storage_time_manager import CloudStorageCleaner
                cleaner = CloudStorageCleaner(self.clouds_client)
                time_dir_path = cleaner.ensure_time_dir_exists(nas_cache_dir)
                
                if not time_dir_path:
                    self.logger.error("创建时间目录失败")
                    raise RuntimeError("Failed to create time directory")
                
                # 构造天翼云盘中的文件名（MD5前缀 + 原始文件名）
                cloud_filename = f"{file_md5.upper()}_{filename}"
                cloud_path = f"{time_dir_path}/{cloud_filename}"
                
                self.logger.info(f"上传文件到云存储路径: {cloud_path}")
                
                # 上传到天翼云盘
                upload_success = self.clouds_client.upload_file(
                    file_data,
                    cloud_path
                )
                
                if not upload_success:
                    self.logger.error(f"文件上传到云存储失败: {cloud_path}")
                    raise RuntimeError("Failed to upload to Tianyi Cloud")
                else:
                    self.logger.info(f"文件上传到云存储成功: {cloud_path}")
                
                # 创建存储记录
                storage = self.storage_repo.create_storage(
                    file_id=file_md5,
                    original_filename=filename,
                    file_size_bytes=len(file_data),
                    media_type=meta.get('media_type', 0),
                    cloud_provider='tianyi',  # 标记为天翼网盘存储
                    file_extension=file_extension,
                    cloud_file_id=cloud_filename,  # 天翼云盘中的文件名
                    local_data=None  # 不再存储本地数据
                )
            except Exception as e:
                # 上传失败，回退到本地存储
                self.logger.warning(f"上传到天翼云盘失败，使用本地存储: {e}")
                storage = self.storage_repo.create_storage(
                    file_id=file_md5,
                    original_filename=filename,
                    file_size_bytes=len(file_data),
                    media_type=meta.get('media_type', 0),
                    cloud_provider='local',  # 标记为本地存储
                    file_extension=file_extension,
                    cloud_file_id=None,
                    local_data=None  # 暂时不存储本地数据
                )
        else:
            self.logger.warning(f"CloudsClient或天翼账号未初始化: clouds_client={self.clouds_client is not None}, tianyi_username={self.tianyi_username}")
            # 没有clouds_client，使用本地存储
            storage = self.storage_repo.create_storage(
                file_id=file_md5,
                original_filename=filename,
                file_size_bytes=len(file_data),
                media_type=meta.get('media_type', 0),
                cloud_provider='local',  # 标记为本地存储
                file_extension=file_extension,
                cloud_file_id=None,
                local_data=None  # 暂时不存储本地数据
            )
        
        # 创建虚拟文件
        virtual_file = self.file_repo.create_file(
            name=filename,
            parent_id=parent_id,
            file_size_bytes=len(file_data),
            media_type=meta.get('media_type', 0),
            file_extension=file_extension,
            storage_file_id=file_md5
        )
        
        self.session.commit()
        return 0
    
    # ==============================
    # 文件下载
    # ==============================
    
    def get_download_url(self, vfs_path: str, expires_in: int = 3600) -> str:
        """
        获取临时下载链接
        
        实现流程：
        1. 查找文件的秒传信息（MD5等）
        2. 通过clouds_sdk查找/nas_cache/目录下是否有以MD5开头的文件
        3. 如果没有，通过秒传接口上传到指定目录
        4. 获取文件的真实下载地址并返回
        5. 更新文件访问时间和下载计数
        """
        # 验证文件存在
        virtual_file = self._resolve_path(vfs_path)
        if not virtual_file or virtual_file.is_directory:
            raise FileNotFoundError(f"File not found: {vfs_path}")
        
        # 获取文件的存储信息
        storage = self.storage_repo.find_by_file_id(virtual_file.storage_file_id)
        if not storage:
            raise FileNotFoundError(f"Storage not found for file: {vfs_path}")
        
        # 更新访问时间和下载计数
        virtual_file.accessed_at = datetime.utcnow()
        self.storage_repo.increment_download_count(storage.id)
        self.session.commit()
        
        # 检查clouds_client是否可用
        if not self.clouds_client:
            raise RuntimeError("Clouds client not initialized")
        
        # 确保token有效，如果无效则尝试重新登录
        if self.tianyi_username:
            if not self._ensure_valid_token(self.tianyi_username):
                raise RuntimeError("Token无效且重新登录失败")
        
        # 获取文件MD5
        file_md5 = storage.file_id.upper()  # 使用大写MD5，与上传时保持一致
        original_filename = virtual_file.name
        
        # 构造天翼云盘中的文件名（MD5前缀 + 原始文件名）
        cloud_filename = f"{file_md5}_{original_filename}"
        nas_cache_dir = "/nas_cache"
        
        # 尝试在不同时间目录中查找文件
        cloud_paths_to_try = []
        
        # 1. 首先尝试当前时间目录
        from src.clouds_sdk.cloud_storage_time_manager import CloudStorageTimeManager
        time_manager = CloudStorageTimeManager()
        current_time_dir = time_manager.get_current_time_dir()
        cloud_paths_to_try.append(f"{nas_cache_dir}/{current_time_dir}/{cloud_filename}")
        
        # 2. 尝试其他可能的时间目录（最近24小时）
        now = datetime.now()
        for i in range(24):
            past_time = now - timedelta(hours=i)
            past_time_dir = past_time.strftime("%Y-%m-%d_%H")
            cloud_paths_to_try.append(f"{nas_cache_dir}/{past_time_dir}/{cloud_filename}")
        
        # 3. 最后尝试旧的路径格式（向后兼容）
        cloud_paths_to_try.append(f"{nas_cache_dir}/{cloud_filename}")
        
        try:
            # 1. 检查天翼云盘中是否已存在该文件（按MD5前缀匹配）
            for cloud_path in cloud_paths_to_try:
                try:
                    # 获取父目录路径
                    parent_dir = "/".join(cloud_path.split("/")[:-1])
                    if parent_dir:
                        # 获取目录中的文件列表
                        files_in_dir = self.clouds_client.ls(parent_dir)
                        
                        # 检查是否有相同MD5前缀的文件
                        for file_item in files_in_dir:
                            if file_item.get('is_dir', False):
                                continue
                            file_name = file_item.get('name', '')
                            # 检查文件名是否以MD5前缀开头
                            if file_name.startswith(file_md5):
                                # 找到相同MD5的文件，构造完整路径
                                found_path = f"{parent_dir}/{file_name}"
                                self.logger.info(f"找到相同MD5的文件: {found_path}")
                                
                                # 直接获取下载链接
                                download_url = self.clouds_client.get_download_url(found_path)
                                self.logger.info(f"在路径找到文件: {found_path}")
                                return download_url
                        
                        # 如果目录中没有相同MD5的文件，尝试直接检测指定路径
                        try:
                            file_info = self.clouds_client.stat(cloud_path)
                            if file_info:
                                # 文件已存在，直接获取下载链接
                                download_url = self.clouds_client.get_download_url(cloud_path)
                                self.logger.info(f"在路径找到文件: {cloud_path}")
                                return download_url
                        except FileNotFoundError:
                            # 文件不存在，继续尝试下一个路径
                            continue
                            
                except Exception as dir_error:
                    self.logger.debug(f"检查目录失败: {cloud_path} - {dir_error}")
                    continue
            
            # 所有路径都找不到文件，需要先上传
            self.logger.info(f"文件在所有时间目录中都不存在，需要先上传: {cloud_filename}")
            
            # 2. 确保nas_cache目录和时间目录存在
            from src.clouds_sdk.cloud_storage_time_manager import CloudStorageCleaner
            cleaner = CloudStorageCleaner(self.clouds_client)
            time_dir_path = cleaner.ensure_time_dir_exists(nas_cache_dir)
            
            if not time_dir_path:
                raise RuntimeError("Failed to create time directory")
            
            # 使用当前时间目录路径
            cloud_path = f"{time_dir_path}/{cloud_filename}"
            # 尝试秒传
            try:
                upload_result = self.clouds_client.quick_upload(
                    storage.file_id.lower(),  # 小写MD5用于秒传
                    cloud_path,
                    file_size=storage.file_size_bytes
                )
                
                if upload_result:
                    # 秒传成功，更新缓存并重新检测文件
                    self.logger.info(f"秒传成功，更新缓存并重新检测文件: {cloud_path}")
                    
                    # 更新当前目录的缓存
                    try:
                        # 获取父目录路径
                        parent_dir = "/".join(cloud_path.split("/")[:-1])
                        if parent_dir:
                            # 清除父目录缓存，强制重新获取文件列表
                            if hasattr(self.clouds_client, 'invalidate_path_cache'):
                                self.clouds_client.invalidate_path_cache(parent_dir)
                                self.logger.debug(f"已清除目录缓存: {parent_dir}")
                            elif hasattr(self.clouds_client, 'clear_cache'):
                                self.clouds_client.clear_cache()
                                self.logger.debug(f"已清除所有缓存")
                    except Exception as cache_error:
                        self.logger.warning(f"清除缓存失败: {cache_error}")
                    
                    # 等待一小段时间让云存储同步
                    import time
                    time.sleep(1.0)  # 增加等待时间到1秒
                    
                    # 重新检测文件是否存在（按MD5前缀匹配）
                    try:
                        # 获取父目录路径
                        parent_dir = "/".join(cloud_path.split("/")[:-1])
                        if parent_dir:
                            # 获取目录中的文件列表
                            files_in_dir = self.clouds_client.ls(parent_dir)
                            
                            # 检查是否有相同MD5前缀的文件
                            for file_item in files_in_dir:
                                if file_item.get('is_dir', False):
                                    continue
                                file_name = file_item.get('name', '')
                                # 检查文件名是否以MD5前缀开头
                                if file_name.startswith(file_md5):
                                    # 找到相同MD5的文件，构造完整路径
                                    found_path = f"{parent_dir}/{file_name}"
                                    self.logger.info(f"秒传后找到相同MD5的文件: {found_path}")
                                    
                                    # 直接获取下载链接
                                    download_url = self.clouds_client.get_download_url(found_path)
                                    self.logger.info(f"秒传后文件检测成功: {found_path}")
                                    return download_url
                        
                        # 如果目录中没有相同MD5的文件，尝试直接检测指定路径
                        try:
                            file_info = self.clouds_client.stat(cloud_path)
                            if file_info:
                                # 文件存在，获取下载链接
                                download_url = self.clouds_client.get_download_url(cloud_path)
                                self.logger.info(f"秒传后文件检测成功: {cloud_path}")
                                return download_url
                        except FileNotFoundError:
                            self.logger.warning(f"秒传后文件检测失败: {cloud_path}")
                            
                    except Exception as stat_error:
                        self.logger.warning(f"秒传后文件检测异常: {stat_error}")
                    
                    # 如果检测失败，仍然尝试获取下载链接
                    try:
                        download_url = self.clouds_client.get_download_url(cloud_path)
                        self.logger.info(f"秒传成功，直接获取下载链接: {cloud_path}")
                        return download_url
                    except Exception as download_error:
                        self.logger.error(f"秒传后获取下载链接失败: {download_error}")
                        raise RuntimeError(f"Failed to get download URL after quick upload: {download_error}")
                else:
                    raise RuntimeError("Quick upload failed")
                    
            except Exception as e:
                # 秒传失败，返回404错误而不是500错误
                # 这表示文件在云存储中不存在，需要先上传
                raise FileNotFoundError(f"File not found in cloud storage: {e}")
                
        except Exception as e:
            raise RuntimeError(f"Failed to get download URL: {e}")
    
    # ==============================
    # 基础文件系统操作
    # ==============================
    
    def mkdir(self, path: str) -> int:
        """创建目录（支持递归创建）"""
        parent_path, dirname = self._split_path(path)
        
        # 处理根目录的情况
        if parent_path == "/":
            parent_id = 1  # 根目录的ID是1
        else:
            parent_file = self._resolve_path(parent_path)
            if not parent_file:
                # 父目录不存在，尝试递归创建
                try:
                    self.mkdir(parent_path)
                    parent_file = self._resolve_path(parent_path)
                    if not parent_file:
                        raise FileNotFoundError(f"Failed to create parent directory: {parent_path}")
                except Exception as e:
                    raise FileNotFoundError(f"Parent directory not found and cannot be created: {parent_path}, error: {e}")
            parent_id = parent_file.id
        
        # 检查目录是否已存在
        existing_dir = self.file_repo.find_by_name_and_parent(dirname, parent_id)
        if existing_dir and not existing_dir.deleted:
            # 目录已存在，不抛出异常（WebDAV兼容性）
            return 0
        
        # 创建目录
        self.file_repo.create_directory(dirname, parent_id)
        self.session.commit()
        return 0
    
    def list_dir(self, path: str) -> List[VFSNode]:
        """列出目录内容"""
        # 处理根目录的情况
        if path == "/" or path == "":
            parent_id = 1  # 根目录的ID是1
        else:
            dir_file = self._resolve_path(path)
            if not dir_file:
                raise FileNotFoundError(f"Directory not found: {path}")
            if not dir_file.is_directory:
                raise NotADirectoryError(f"Not a directory: {path}")
            parent_id = dir_file.id
        
        children = self.file_repo.find_children(parent_id)
        # 过滤掉已删除的文件，并且过滤掉重复的文件（根据name去重）
        children = [child for child in children if not child.deleted]
        
        # 去重：如果有相同名称的文件，保留最新的（id最大的）
        unique_children = {}
        for child in children:
            name = child.name or ""  # 处理空名称
            if name not in unique_children or child.id > unique_children[name].id:
                unique_children[name] = child
        
        # 过滤掉空名称的文件（除非是根目录本身）
        filtered_children = []
        for name, child in unique_children.items():
            if name or child.id == 1:  # 保留非空名称的文件，或者根目录本身
                filtered_children.append(child)
        
        nodes = []
        for child in filtered_children:
            storage = None
            if not child.is_directory and child.storage_file_id:
                storage = self.storage_repo.find_by_file_id(child.storage_file_id)
            nodes.append(VFSNode.from_virtual_file(child, storage))
        
        return nodes
    
    def get_info(self, path: str) -> VFSNode:
        """获取文件/目录信息"""
        virtual_file = self._resolve_path(path)
        if not virtual_file:
            raise FileNotFoundError(f"Path not found: {path}")
        
        storage = None
        if not virtual_file.is_directory and virtual_file.storage_file_id:
            storage = self.storage_repo.find_by_file_id(virtual_file.storage_file_id)
        
        return VFSNode.from_virtual_file(virtual_file, storage)
    
    def _resolve_path(self, path: str) -> Optional[VirtualFile]:
        """解析路径到VirtualFile对象"""
        if path == "/" or path == "":
            # 根目录：查找ID=1的根目录记录，如果不存在则返回虚拟根目录
            root_file = self.file_repo.get_by_id(1)
            if root_file and root_file.is_directory:
                return root_file
            # 如果没有ID=1的根目录，返回None表示根目录
            return None
        
        path = path.strip("/")
        parts = path.split("/")
        
        current_parent_id = 1  # 从根目录(ID=1)开始
        current_file = None
        
        for part in parts:
            # 查找时不过滤已删除的文件，让上层调用者决定如何处理
            current_file = self.file_repo.find_by_name_and_parent(part, current_parent_id)
            if not current_file:
                return None
            # find_by_name_and_parent 已经过滤了已删除的文件，不需要再次检查
            current_parent_id = current_file.id
        
        return current_file
    
    def _split_path(self, path: str) -> tuple[str, str]:
        """分割路径为父路径和文件名"""
        path = path.strip("/")
        if "/" not in path:
            return "/", path
        
        parts = path.split("/")
        filename = parts[-1]
        parent_path = "/" + "/".join(parts[:-1]) if len(parts) > 1 else "/"
        return parent_path, filename
    
    # ==============================
    # 文件/目录变更操作
    # ==============================
    
    def move(self, src: str, dst: str, overwrite: bool = False) -> bool:
        """移动文件或目录"""
        src_file = self._resolve_path(src)
        if not src_file or src_file.deleted:
            raise FileNotFoundError(f"Source not found: {src}")
        
        # 解析目标路径
        dst_parent_path, dst_name = self._split_path(dst)
        dst_parent = self._resolve_path(dst_parent_path) if dst_parent_path != "/" else None
        dst_parent_id = dst_parent.id if dst_parent else 1  # 根目录的ID是1
        
        # 检查目标是否已存在
        existing_dst = self.file_repo.find_by_name_and_parent(dst_name, dst_parent_id)
        if existing_dst:
            if overwrite:
                # 覆盖模式：先删除目标文件
                self.logger.info(f"覆盖模式：删除目标文件 {dst}")
                existing_dst.deleted = True
                existing_dst.deleted_at = datetime.utcnow()
                self.session.commit()
            else:
                raise FileExistsError(f"Destination already exists: {dst}")
        
        # 执行移动：更新源文件的位置和名称
        success = self.file_repo.move_file(src_file.id, dst_parent_id, dst_name)
        if success:
            self.session.commit()
            self.logger.info(f"文件移动成功: {src} -> {dst}")
        return success
    
    def copy(self, src: str, dst: str, overwrite: bool = False) -> int:
        """复制文件或目录"""
        src_file = self._resolve_path(src)
        if not src_file or src_file.deleted:
            raise FileNotFoundError(f"Source not found: {src}")
        
        # 解析目标路径
        dst_parent_path, dst_name = self._split_path(dst)
        dst_parent = self._resolve_path(dst_parent_path) if dst_parent_path != "/" else None
        dst_parent_id = dst_parent.id if dst_parent else 1  # 根目录的ID是1
        
        # 检查目标是否已存在
        existing_dst = self.file_repo.find_by_name_and_parent(dst_name, dst_parent_id)
        if existing_dst:
            if overwrite:
                # 覆盖模式：先删除目标文件
                self.logger.info(f"覆盖模式：删除目标文件 {dst}")
                existing_dst.deleted = True
                existing_dst.deleted_at = datetime.utcnow()
                self.session.commit()
            else:
                raise FileExistsError(f"Destination already exists: {dst}")
        
        if src_file.is_directory:
            # 复制目录
            new_dir = self.file_repo.create_directory(dst_name, dst_parent_id)
            
            # 递归复制子项
            children = self.file_repo.find_children(src_file.id)
            for child in children:
                if not child.deleted:
                    child_src_path = src + "/" + child.name
                    child_dst_path = dst + "/" + child.name
                    self.copy(child_src_path, child_dst_path, overwrite=True)
        else:
            # 复制文件
            new_file = self.file_repo.create_file(
                name=dst_name,
                parent_id=dst_parent_id,
                file_size_bytes=src_file.file_size_bytes,
                media_type=src_file.media_type,
                file_extension=src_file.file_extension,
                storage_file_id=src_file.storage_file_id,
                large_icon_url=src_file.large_icon_url,
                small_icon_url=src_file.small_icon_url
            )
            
            # 如果有存储文件，增加引用计数
            if src_file.storage_file_id:
                storage = self.storage_repo.find_by_file_id(src_file.storage_file_id)
                if storage:
                    self.storage_repo.increment_reference_count(storage.id)
        
        self.session.commit()
        return 0
    
    def rename(self, path: str, new_name: str) -> bool:
        """重命名文件或目录"""
        virtual_file = self._resolve_path(path)
        if not virtual_file or virtual_file.deleted:
            raise FileNotFoundError(f"Path not found: {path}")
        
        # 检查新名称是否已存在
        existing_file = self.file_repo.find_by_name_and_parent(new_name, virtual_file.parent_id)
        if existing_file and not existing_file.deleted and existing_file.id != virtual_file.id:
            raise FileExistsError(f"Name already exists: {new_name}")
        
        # 执行重命名
        success = self.file_repo.move_file(virtual_file.id, virtual_file.parent_id, new_name)
        if success:
            self.session.commit()
        return success
    
    def delete(self, path: str, force: bool = False, recursive: bool = False) -> bool:
        """删除文件或目录"""
        virtual_file = self._resolve_path(path)
        if not virtual_file or virtual_file.deleted:
            raise FileNotFoundError(f"Path not found: {path}")
        
        if force:
            # 强制删除（永久删除）
            return self._permanent_delete(virtual_file, recursive)
        else:
            # 移动到回收站
            return self._move_to_trash(virtual_file, recursive)
    
    def _move_to_trash(self, virtual_file: VirtualFile, recursive: bool = False) -> bool:
        """移动到回收站 - 直接删除，不使用事务"""
        if virtual_file.is_directory and not recursive:
            # 检查目录是否为空
            children = self.file_repo.find_children(virtual_file.id)
            active_children = [child for child in children if not child.deleted]
            if active_children:
                raise OSError("Directory not empty")
        
        # 设置删除标记和原始信息
        virtual_file.deleted = True
        virtual_file.deleted_at = datetime.utcnow()
        virtual_file.original_parent_id = virtual_file.parent_id
        virtual_file.original_name = virtual_file.name
        
        if virtual_file.is_directory and recursive:
            # 递归删除子项
            children = self.file_repo.find_children(virtual_file.id)
            for child in children:
                if not child.deleted:
                    self._move_to_trash(child, recursive=True)
        
        # 直接提交，不使用事务管理
        self.session.commit()
        return True
    
    def _permanent_delete(self, virtual_file: VirtualFile, recursive: bool = False) -> bool:
        """永久删除 - 直接删除，不使用事务管理"""
        if virtual_file.is_directory:
            if not recursive:
                # 检查目录是否为空
                children = self.file_repo.find_children(virtual_file.id)
                active_children = [child for child in children if not child.deleted]
                if active_children:
                    raise OSError("Directory not empty")
            else:
                # 递归删除子项
                children = self.file_repo.find_children(virtual_file.id)
                for child in children:
                    self._permanent_delete(child, recursive=True)
        else:
            # 删除云存储中的文件
            if virtual_file.storage_file_id and self.clouds_client:
                storage = self.storage_repo.find_by_file_id(virtual_file.storage_file_id)
                if storage:
                    try:
                        # 构造云存储中的文件路径
                        file_md5 = storage.file_id.upper()
                        original_filename = virtual_file.name
                        cloud_filename = f"{file_md5}_{original_filename}"
                        nas_cache_dir = "/nas_cache"
                        
                        # 尝试在不同时间目录中查找并删除文件
                        cloud_paths_to_try = []
                        
                        # 1. 首先尝试当前时间目录
                        from src.clouds_sdk.cloud_storage_time_manager import CloudStorageTimeManager
                        time_manager = CloudStorageTimeManager()
                        current_time_dir = time_manager.get_current_time_dir()
                        cloud_paths_to_try.append(f"{nas_cache_dir}/{current_time_dir}/{cloud_filename}")
                        
                        # 2. 尝试其他可能的时间目录（最近24小时）
                        now = datetime.now()
                        for i in range(24):
                            past_time = now - timedelta(hours=i)
                            past_time_dir = past_time.strftime("%Y-%m-%d_%H")
                            cloud_paths_to_try.append(f"{nas_cache_dir}/{past_time_dir}/{cloud_filename}")
                        
                        # 3. 最后尝试旧的路径格式（向后兼容）
                        cloud_paths_to_try.append(f"{nas_cache_dir}/{cloud_filename}")
                        
                        # 尝试删除文件
                        file_deleted = False
                        for cloud_path in cloud_paths_to_try:
                            try:
                                if self.clouds_client.exists(cloud_path):
                                    self.clouds_client.rm(cloud_path)
                                    self.logger.info(f"删除云存储文件成功: {cloud_path}")
                                    file_deleted = True
                                    break
                            except Exception as e:
                                self.logger.debug(f"删除云存储文件失败: {cloud_path} - {e}")
                                continue
                        
                        if not file_deleted:
                            self.logger.warning(f"未找到云存储文件进行删除: {cloud_filename}")
                            
                    except Exception as e:
                        self.logger.error(f"删除云存储文件时发生错误: {e}")
            
            # 减少存储引用计数，但不删除存储信息
            # 即使引用计数为0，也保留存储信息以支持秒传检测
            if virtual_file.storage_file_id:
                storage = self.storage_repo.find_by_file_id(virtual_file.storage_file_id)
                if storage:
                    # 只减少引用计数，不删除存储记录
                    self.storage_repo.decrement_reference_count(storage.id)
                    self.logger.debug(f"减少存储引用计数: {storage.file_id}, 当前引用计数: {storage.reference_count}")
        
        # 直接删除虚拟文件记录
        self.session.delete(virtual_file)
        self.session.commit()
        return True
    
    # ==============================
    # 回收站操作
    # ==============================
    
    def list_trash(self) -> List[VFSNode]:
        """列出回收站中的文件"""
        # 查找所有已删除的文件
        deleted_files = self.session.query(VirtualFile).filter(
            VirtualFile.deleted == True
        ).all()
        
        nodes = []
        for deleted_file in deleted_files:
            storage = None
            if not deleted_file.is_directory and deleted_file.storage_file_id:
                storage = self.storage_repo.find_by_file_id(deleted_file.storage_file_id)
            nodes.append(VFSNode.from_virtual_file(deleted_file, storage))
        
        return nodes
    
    def restore_from_trash_by_original_path(self, original_path: str, target_path: Optional[str] = None) -> bool:
        """根据原始路径从回收站恢复文件"""
        # 查找已删除的文件
        deleted_file = self.session.query(VirtualFile).filter(
            VirtualFile.deleted == True,
            VirtualFile.original_name == original_path.split('/')[-1]
        ).first()
        
        if not deleted_file:
            raise FileNotFoundError(f"File not found in trash: {original_path}")
        
        if target_path:
            # 恢复到指定路径
            parent_path, filename = self._split_path(target_path)
            parent_file = self._resolve_path(parent_path) if parent_path != "/" else None
            parent_id = parent_file.id if parent_file else None
            
            # 检查目标是否已存在
            existing_file = self.file_repo.find_by_name_and_parent(filename, parent_id)
            if existing_file and not existing_file.deleted:
                raise FileExistsError(f"Target already exists: {target_path}")
            
            deleted_file.parent_id = parent_id
            deleted_file.name = filename
        else:
            # 恢复到原始位置
            deleted_file.parent_id = deleted_file.original_parent_id
            deleted_file.name = deleted_file.original_name
            
            # 检查原始位置是否已被占用
            existing_file = self.file_repo.find_by_name_and_parent(
                deleted_file.original_name, deleted_file.original_parent_id
            )
            if existing_file and not existing_file.deleted:
                raise FileExistsError(f"Original location is occupied: {original_path}")
        
        # 清除删除标记
        deleted_file.deleted = False
        deleted_file.deleted_at = None
        deleted_file.original_parent_id = None
        deleted_file.original_name = None
        
        # 如果是目录，递归恢复子项
        if deleted_file.is_directory:
            self._restore_children_from_trash(deleted_file.id)
        
        self.session.commit()
        return True
    
    def _restore_children_from_trash(self, parent_id: int):
        """递归恢复子项"""
        children = self.session.query(VirtualFile).filter(
            VirtualFile.parent_id == parent_id,
            VirtualFile.deleted == True
        ).all()
        
        for child in children:
            child.deleted = False
            child.deleted_at = None
            child.original_parent_id = None
            child.original_name = None
            
            if child.is_directory:
                self._restore_children_from_trash(child.id)
    
    def delete_from_trash_by_original_path(self, original_path: str) -> bool:
        """根据原始路径从回收站永久删除文件"""
        # 查找已删除的文件
        deleted_file = self.session.query(VirtualFile).filter(
            VirtualFile.deleted == True,
            VirtualFile.original_name == original_path.split('/')[-1]
        ).first()
        
        if not deleted_file:
            raise FileNotFoundError(f"File not found in trash: {original_path}")
        
        # 永久删除
        return self._permanent_delete(deleted_file, recursive=True)
    
    def empty_trash(self) -> int:
        """清空回收站"""
        deleted_files = self.session.query(VirtualFile).filter(
            VirtualFile.deleted == True
        ).all()
        
        count = 0
        for deleted_file in deleted_files:
            self._permanent_delete(deleted_file, recursive=True)
            count += 1
        
        return count
    
    def get_trash_item_by_original_path(self, original_path: str) -> VFSNode:
        """根据原始路径获取回收站中的文件信息"""
        deleted_file = self.session.query(VirtualFile).filter(
            VirtualFile.deleted == True,
            VirtualFile.original_name == original_path.split('/')[-1]
        ).first()
        
        if not deleted_file:
            raise FileNotFoundError(f"File not found in trash: {original_path}")
        
        storage = None
        if not deleted_file.is_directory and deleted_file.storage_file_id:
            storage = self.storage_repo.find_by_file_id(deleted_file.storage_file_id)
        
        return VFSNode.from_virtual_file(deleted_file, storage)